package com.dingwen.treasure.core.util;

import lombok.experimental.UtilityClass;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.Arrays;

/**
 * 　AspectUtil: 切面工具类
 * 　@author dingwen
 * 　@date 2022/6/24
 */
@UtilityClass
public class AspectUtil {

    /**
     * .（区分基本类型和引用数据类型分别获取实体参数值）
     */
    private static final String POINT = ".";
    /**
     * #（EL 参数指定前缀）
     */
    private static final String JIN = "#";


    /**
     * Spring EL 表达式解析器
     */
    private static ExpressionParser expressionParser = new SpelExpressionParser();

    /**
     * 通过el解析方法真实值
     *
     * @param joinPoint 连接点
     * @param tClazz    返回值类型
     * @param el        el模版
     */
    public  <T> T getValueByEL(ProceedingJoinPoint joinPoint, Class<T> tClazz, String el) {
        // 所有形势参数名称集合
         String[] names = getFormalParameterNames(joinPoint);
        //当前需解析参数所在参数当位置
         Integer index = getIndexForKeyParamsInFormalParameterNames(el, names);

        // 当前方法所有真实入参
         Object[] args = getArgs(joinPoint);

        return getValue(el, names[index], args[index], tClazz);

    }


    /**
     * 获得具体参数值
     *
     * @param paramPlaceholder 参数占位符
     * @param paramName        参数名称
     * @param arg              参数值
     * @return {@link String}
     */
    public  <T> T getValue(String paramPlaceholder, String paramName, Object arg, Class<T> tClazz) {
        Expression expression = expressionParser.parseExpression(paramPlaceholder);
        StandardEvaluationContext context = new StandardEvaluationContext();
        context.setVariable(paramName, arg);
        return (T) expression.getValue(context, tClazz);
    }

    /**
     * Get Args: 方法所有真实入参值
     *
     * @param joinPoint 连接点
     * @return {@link Object[]}
     */
    public  Object[] getArgs(ProceedingJoinPoint joinPoint) {
        return joinPoint.getArgs();
    }


    /**
     * 获取形式参数名称集合
     *
     * @return {@link String[]}
     */
    public  String[] getFormalParameterNames(ProceedingJoinPoint joinPoint) {
        Method method = ((MethodSignature) (joinPoint.getSignature())).getMethod();
        Parameter[] parameters = method.getParameters();
        int length = parameters.length;
        String[] formalParameterNames = new String[length];
        for (int i = length - 1; i >= 0; i--) {
            formalParameterNames[i] = parameters[i].getName();
        }
        return formalParameterNames;
    }


    /**
     * 获取索引(区分获取引用类型和基本类型对于的key参数名称在形式参数名称数组中的下标) </br>
     * <p>注意注解中的对象名称必须和方法的形式参数一致</p>
     *
     * @param coreParamName        核心参数名（注解上面标注的）
     * @param formalParameterNames 形式参数名（方法参数）
     * @return {@link Integer}
     */
    public  Integer getIndexForKeyParamsInFormalParameterNames(String coreParamName, String[] formalParameterNames) {
        // 基本类型
        String paramName = coreParamName.substring(coreParamName.indexOf(JIN) + 1);
        // 引用对象类型
        if (coreParamName.contains(POINT)) {
            int startIndex = coreParamName.indexOf(JIN) + 1;
            int endIndex = coreParamName.indexOf(POINT);
            paramName = coreParamName.substring(startIndex, endIndex);
        }
        return Arrays.asList(formalParameterNames).indexOf(paramName);
    }
}
